 /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.tetris;

import java.awt.Point;
import java.util.ArrayList;
import sirtet.*;

/**
 * A game (data, not visualized) board for Tetris
 * @author guangwei.zhu
 */
public class TetrisBoard extends GameBoard {

    public static final String GAMETYPE = "tetris";

    public TetrisBoard(int width, int height) {
        super(width, height);
    }

    /**
     * Initialize the TetrisBoard area.
     */
    @Override
    protected void initBlocks() {
        int i, j;

        for (i = 0; i < boardHeight; i++) {
            for (j = 0; j < boardWidth; j++) {
                blocks[i][j] = (Integer) 0;
            }
        }
    }

    /**
     * Process the triggered action.
     * @param action the action triggered which needs to be processed
     * @return true if the action is valid and processed.
     */
    @Override
    public synchronized boolean doAction(GameAction action) {

        if (!isStarted) {
            // Game is currently dead
            return false;
        }

        String command = action.command;

        if (command.equals(TetrisAction.NOOP)) {
            return true;        // Bypass null command
            } else if (command.equals(TetrisAction.LEFT)) {
            if (this.canMoveLeft()) {
                currentPosition.x--;
            } else {
                return false;
            }
        } else if (command.equals(TetrisAction.RIGHT)) {
            if (this.canMoveRight()) {
                currentPosition.x++;
            } else {
                return false;
            }
        } else if (command.equals(TetrisAction.ROTATE)) {
            if (this.canRotate()) {
                ((TetrisPiece) currentPiece).rotate();
            } else {
                return false;
            }
        } else if (command.equals(TetrisAction.FALL)) {
            if (this.canFall()) {
                currentPosition.y++;
            } else {
                try {
                    fixPiece();
                    if (!this.isAlive()) { // Gameover
                        return true;
                    }
                } catch (IllegalStateException ex) {
                    System.out.println(ex);
                    this.gameover();
                    return false;
                }
            }
        } else {    // Not a valid command
            return false;
        }
        notifyActionTriggered(action);
        return true;
    }

    @Override
    protected synchronized void afterNextPiece() {
        currentPosition.y = 0;
        currentPosition.x = (this.boardWidth - currentPiece.getWidth()) / 2;

        // If overlapping occurs when a new piece shows up
        if (this.isOverlapped(currentPiece, currentPosition)) {
            gameover();
            return;
        }

        this.addNextPiece(this.pieceProvider.requestPiece());
    }

    @Override
    protected synchronized boolean beforeNextPiece() {
        // Stub function, left for overriding
        return true;
    }

    @Override
    protected synchronized boolean beforeFixingPiece() {
        // Stub function, left for overriding
        return true;
    }

    @Override
    protected synchronized boolean eliminate() {
        int i, j, count;
        ArrayList<Integer> eliminatedLines = new ArrayList<Integer>();

        for (i = 0; i < this.boardHeight; i++) {
            count = 0;
            for (j = 0; j < this.boardWidth; j++) {
                if ((Integer) this.blocks[i][j] == TetrisPiece.EMPTY) {
                    break;
                } else {
                    count++;
                }
            }

            // Check if one line is full
            if (count == this.boardWidth) {
                eliminatedLines.add(i);
            }
        }

        // Shift down lines
        for (int line : eliminatedLines) {
            for (i = line; i > 0; i--) {
                System.arraycopy(blocks[i - 1], 0, blocks[i], 0, this.boardWidth);
            }
            for (j = 0; j < this.boardWidth; j++) {
                blocks[0][j] = TetrisPiece.EMPTY;
            }
        }

        if (eliminatedLines.size() > 0) {
            // Notify the listeners about elimination
            notifyBlocksEliminated(eliminatedLines.toArray());
            return true;
        } else {
            return false;
        }

    }

    /**
     * Test whether the current piece can move left.
     * @return true if the piece can be moved to the left; false otherwise
     */
    public synchronized boolean canMoveLeft() {
        Point testPosition = new Point(currentPosition);
        testPosition.x--;

        return !this.isOverlapped(currentPiece, testPosition);
    }

    /**
     * Test whether the current piece can move right.
     * @return true if the piece can be moved to the right; false otherwise
     */
    public synchronized boolean canMoveRight() {
        Point testPosition = new Point(currentPosition);
        testPosition.x++;

        return !this.isOverlapped(currentPiece, testPosition);
    }

    /**
     * Test whether the current piece can rotate.
     * @return true if the piece can be rotated; false otherwise
     */
    public synchronized boolean canRotate() {
        TetrisPiece pieceCopy = (TetrisPiece) ((TetrisPiece) currentPiece).clone();
        pieceCopy.rotate();

        return !this.isOverlapped(pieceCopy, currentPosition);
    }

    /**
     * Test whether the current piece can fall down by 1 block.
     * @return true if the piece can fall; false otherwise
     */
    public synchronized boolean canFall() {
        Point testPosition = new Point(currentPosition);
        testPosition.y++;

        return !this.isOverlapped(currentPiece, testPosition);
    }
}
